package Auther

import (
	"Yoj/internal/dao"
	"Yoj/internal/model/do"
	"Yoj/internal/model/entity"
	"github.com/gogf/gf/v2/errors/gcode"
	"github.com/gogf/gf/v2/errors/gerror"
	"github.com/gogf/gf/v2/os/glog"
	"github.com/gogf/gf/v2/os/gtime"
	"github.com/golang-jwt/jwt/v5"
	"time"

	"context"
)

// 1. 生成token;
// 2. 验证token;
// 3. 失效token;

// TODO: 记得mysql设置一下自动清除过期的token
const (
	JWTsecret    = "WeekSecret"
	JWTAliveTime = 24 * 60 * 60 // 24小时
)

type AuthToken struct {
	ctx context.Context
}

func NewAuthToken(ctx context.Context) *AuthToken {
	return &AuthToken{ctx: ctx}
}

type JWTClaims struct {
	UserId uint `json:"userId"`
	jwt.RegisteredClaims
}

// 生成Token
func (a *AuthToken) GenerateToken(userId uint) (string, error) {
	claims := &JWTClaims{
		UserId: userId,
		RegisteredClaims: jwt.RegisteredClaims{
			ExpiresAt: jwt.NewNumericDate(time.Now().Add(JWTAliveTime)),
			IssuedAt:  jwt.NewNumericDate(time.Now()),
			NotBefore: jwt.NewNumericDate(time.Now()),
		},
	}

	token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
	signedToken, err := token.SignedString([]byte(JWTsecret))
	if err != nil {
		glog.Error(a.ctx, err)
		return "", gerror.NewCode(gcode.CodeInternalError, err.Error())
	}

	// 将token存入数据库
	_, err = dao.Tokens.Ctx(a.ctx).Data(do.Tokens{
		Token:     signedToken,
		UserId:    userId,
		StartTime: gtime.Now(),
		EndTime:   gtime.Now().Add(JWTAliveTime),
	}).Insert()
	if err != nil {
		glog.Error(a.ctx, err)
		return "", gerror.NewCode(gcode.CodeInternalError, err.Error())
	}

	return signedToken, nil
}

// 验证Token
func (a *AuthToken) ValidateToken(tokenString string) (bool, error) {
	// 验证JWT
	if len(tokenString) > 7 && tokenString[:7] == "Bearer " {
		tokenString = tokenString[7:]
	}

	claims := &JWTClaims{}
	token, err := jwt.ParseWithClaims(tokenString, claims, func(token *jwt.Token) (interface{}, error) {
		return []byte(JWTsecret), nil
	})

	if err != nil || !token.Valid {
		return false, gerror.NewCode(gcode.CodeNotAuthorized, "Token无效或者已经过期")
	}

	// 从数据库中验证Token
	tokeninfo := &entity.Tokens{}
	err = dao.Tokens.Ctx(a.ctx).Where("token = ?", tokenString).Scan(tokeninfo)
	if err != nil {
		glog.Error(a.ctx, err)
		return false, gerror.NewCode(gcode.CodeInternalError, "Token查询失败")
	}

	if tokeninfo == nil || tokeninfo.Alive == 0 || tokeninfo.EndTime.Timestamp() < gtime.Now().Timestamp() {
		glog.Error(a.ctx, err)
		return false, gerror.NewCode(gcode.CodeInternalError, "Token过期")
	}

	return true, nil
}

// 失效Token
func (a *AuthToken) InvalidateToken(tokenString string) error {
	_, err := dao.Tokens.Ctx(a.ctx).Where("token = ?", tokenString).Delete()
	if err != nil {
		glog.Error(a.ctx, err)
		return gerror.NewCode(gcode.CodeInternalError, "Token删除失败")
	}
	return nil
}

// 删除所有Token
func (a *AuthToken) DeleteAllTokens(userId uint) error {
	_, err := dao.Tokens.Ctx(a.ctx).Where("user_id = ?", userId).Delete()
	if err != nil {
		glog.Error(a.ctx, err)
		return gerror.NewCode(gcode.CodeInternalError, "Token删除失败")
	}
	return nil
}
